Skip to content

Add Generic<'a, T> type alias for generic parameters in _Impl traits#4327

Merged
kennykerr merged 8 commits intomasterfrom
copilot/improve-generics-bindings
Apr 29, 2026
Merged

Add Generic<'a, T> type alias for generic parameters in _Impl traits#4327
kennykerr merged 8 commits intomasterfrom
copilot/improve-generics-bindings

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 29, 2026

What's this all about?

This PR implements an improved version of the generics binding improvement from #4155, with the cleaner name Generic instead of ImplParam.

Problem

When implementing generic WinRT interfaces like IMap_Impl<K, V> or IVector_Impl<T>, users had to write Ref<K> for all generic type parameters — even primitives where Ref adds no semantic value:

// Before
impl IMap_Impl<i32, f32> for Map_Impl {
    fn HasKey(&self, key: Ref<i32>) -> Result<bool> { ... }   // unnecessary Ref for i32
    fn Insert(&self, key: Ref<i32>, value: Ref<f32>) -> Result<bool> { ... }
}

The same issue affected event handler closures: TypedEventHandler::new(move |_, args: Ref<i32>| { assert_eq!(*args, 2); }).

Solution

Added a Generic<'a> generic associated type (GAT) to the Type trait in windows-core:

  • For CopyType (primitives, GUID, enums, etc.): Generic<'a> = T — the type itself, no Ref wrapper
  • For CloneType (HSTRING, etc.): Generic<'a> = Ref<'a, T> — same as before
  • For InterfaceType (COM interfaces): Generic<'a> = Ref<'a, T> — same as before

The GAT is named Generic<'a> to avoid confusion with the existing Param trait.

A public type alias is exported:

pub type Generic<'a, T> = <T as Type<T>>::Generic<'a>;

A #[doc(hidden)] free function generic_as_default is exported from windows_core to convert a Generic<'_, T> reference to a &T::Default reference. This is needed by generic collection implementations (IMap_Impl, IVector_Impl, etc.) because Generic<'_, T> is either T itself (for CopyType) or Ref<'_, T> (for other types), and there is no built-in Rust construct that dereferences uniformly across both forms. The function is a zero-cost reinterpretation since Generic<'_, T> and T::Default always share the same memory layout. Generated vtable code uses core::mem::transmute_copy directly instead of a trait helper method.

Result

Users can now write generic type parameters directly for primitives:

// After
impl IMap_Impl<i32, f32> for Map_Impl {
    fn HasKey(&self, key: i32) -> Result<bool> { ... }        // i32 directly!
    fn Insert(&self, key: i32, value: f32) -> Result<bool> { ... }
}

// Event handler closures:
TypedEventHandler::new(move |_, args: i32| { assert_eq!(args, 2); Ok(()) })

For non-primitive types like HSTRING and COM interfaces, Generic<'_, T> resolves to Ref<'_, T> as before.

Changes

  • crates/libs/core/src/type.rs — Added Generic<'a> GAT, pub type Generic<'a, T>, and a standalone generic_as_default free function; removed abi_to_generic and generic_as_default trait methods from Type
  • crates/libs/bindgen/src/types/method.rs — Updated code generator to emit Generic<'_, T> in _Impl signatures and use core::mem::transmute_copy in vtable upcall code
  • Regenerated collections/bindings.rs, future/bindings.rs, Foundation/mod.rs, class_dep.rs, interface_iterable.rs, delegate_generic.rs, reference_custom/bindings.rs, reference_no_deps/bindings.rs
  • Updated hand-written collection implementations (map.rs, vector.rs, etc.) to use Generic<'_, T> and the generic_as_default free function
  • Updated tests to use primitive types directly and removed TODO comments

Copilot AI and others added 2 commits April 29, 2026 16:50
Comment thread crates/libs/core/src/type.rs Outdated
/// since these are cheap to copy and `Ref` adds no value. For `CloneType` (HSTRING, etc.)
/// and `InterfaceType` (COM interfaces) this is `Ref<'a, T>` — a borrowed wrapper that
/// matches the ABI representation.
type Param<'a>: 'a
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot might be clearer if this were just called Generic as well otherwise it can be confused with the Param trait.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 2a23e69. Renamed the GAT from type Param<'a> to type Generic<'a> on the Type trait so it matches the public alias and can't be confused with the Param trait. The pub type Generic<'a, T> alias now resolves to <T as Type<T>>::Generic<'a>.

@kennykerr kennykerr marked this pull request as ready for review April 29, 2026 17:24
Copilot AI requested a review from kennykerr April 29, 2026 17:24
Comment thread crates/libs/core/src/type.rs Outdated
/// # Safety
///
/// The ABI value must be valid for the lifetime `'a`.
unsafe fn abi_to_param(abi: &Self::Abi) -> Self::Generic<'_>;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename abi_to_param to abi_to_generic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in f0adad2. Renamed abi_to_paramabi_to_generic throughout.

Comment thread crates/libs/core/src/type.rs Outdated
/// This is used in generic collection implementations (e.g. `IMap_Impl`, `IVector_Impl`)
/// to obtain a `&K::Default` from a `Generic<'_, K>` in order to perform map/vector
/// operations that operate on the default type.
fn param_as_default<'a, 'b>(param: &'a Self::Generic<'b>) -> &'a Self::Default;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename param_as_default to generic_as_default.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in f0adad2. Renamed param_as_defaultgeneric_as_default throughout.

@kennykerr
Copy link
Copy Markdown
Collaborator

@copilot deal with review comments

Copilot AI requested a review from kennykerr April 29, 2026 17:32
Comment thread crates/libs/core/src/type.rs Outdated
unsafe { core::mem::transmute_copy(abi) }
}

fn generic_as_default<'a, 'b>(param: &'a Ref<'b, Self>) -> &'a Option<Self> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the generic_as_default implementations appear to be passthroughs - do we really need it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from the Type trait in f036bc1. The reason the implementations are all passthroughs is that Generic<'_, T> and T::Default always share the same memory layout (identity for CopyType; Ref<T> is #[repr(transparent)] over T::Abi which is layout-compatible with T::Default for Ref types). The logic is still needed in generic collection code to uniformly convert Generic<'_, K>&K::Default, so it's now a standalone #[doc(hidden)] free function generic_as_default::<T>(&key) exported from windows_core rather than a trait method.

Comment thread crates/libs/core/src/type.rs Outdated
/// # Safety
///
/// The ABI value must be valid for the lifetime `'a`.
unsafe fn abi_to_generic(abi: &Self::Abi) -> Self::Generic<'_>;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The abi_to_generic doesn't appear to provide any more value over transmute_copy - can we do without?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in f036bc1. All call sites now use core::mem::transmute_copy directly, and the method is gone from the trait.

@kennykerr
Copy link
Copy Markdown
Collaborator

@copilot deal with review comments

…neric_as_default to free function; use transmute_copy for abi conversion

Agent-Logs-Url: https://github.com/microsoft/windows-rs/sessions/274af45a-45d3-4cc5-91eb-4470139b56d6

Co-authored-by: kennykerr <9845234+kennykerr@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 29, 2026

@copilot deal with review comments

Done in f036bc1. Both abi_to_generic and generic_as_default are removed from the Type trait. abi_to_generic call sites now use core::mem::transmute_copy directly. generic_as_default is kept as a #[doc(hidden)] free function (not a trait method) since the conversion from Generic<'_, T> to &T::Default is still needed in generic collection code.

Copilot AI requested a review from kennykerr April 29, 2026 18:24
@kennykerr kennykerr merged commit d50128c into master Apr 29, 2026
28 checks passed
@kennykerr kennykerr deleted the copilot/improve-generics-bindings branch April 29, 2026 19:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants